package com.yyfax.common.recyclerview.view;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;

import com.yyfax.common.CLog;
import com.yyfax.common.R;
import com.yyfax.common.recyclerview.base.*;
import com.yyfax.common.recyclerview.base.ItemViewDelegateManager;
import com.yyfax.common.recyclerview.listener.OnLoadMoreListener;
import com.yyfax.common.recyclerview.listener.OnLoadMoreScrollListener;
import com.yyfax.common.recyclerview.listener.OnRefreshListener;
import com.yyfax.common.recyclerview.listener.SimpleAnimatorListener;
import com.yyfax.common.recyclerview.wrapper.StatesAdapter;
import com.yyfax.common.recyclerview.wrapper.WrapperAdapter;

/**
 * Created by aspsine on 16/3/3.
 * https://github.com/Aspsine/IRecyclerView
 */
public class IRecyclerView extends RecyclerView {
	private static final String TAG = IRecyclerView.class.getSimpleName();

	public static final int STATUS_DEFAULT = 0;
	public static final int STATUS_SWIPING_TO_REFRESH = 1;
	public static final int STATUS_RELEASE_TO_REFRESH = 2;
	public static final int STATUS_REFRESHING = 3;

	private int mStatus;//当前状态

	private boolean mIsAutoRefreshing;

	private boolean mRefreshEnabled;
	private boolean mLoadMoreEnabled;

	private int mRefreshFinalMoveOffset;
	private int mActivePointerId = -1;
	private int mLastTouchX = 0;
	private int mLastTouchY = 0;

	private OnRefreshListener mOnRefreshListener;
	private OnLoadMoreListener mOnLoadMoreListener;

	private RefreshHeaderLayout mRefreshHeaderContainer;
	private View mRefreshHeaderView;

	private FrameLayout mLoadMoreFooterContainer;
	private View mLoadMoreFooterView;

	private ValueAnimator mScrollAnimator;

	private ItemViewDelegateManager mItemViewDelegateManager;

	public IRecyclerView(Context context) {
		this(context, null);
	}

	public IRecyclerView(Context context, @Nullable AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public IRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IRecyclerView, defStyle, 0);
		mRefreshEnabled = a.getBoolean(R.styleable.IRecyclerView_refreshEnabled, true);
		mLoadMoreEnabled = a.getBoolean(R.styleable.IRecyclerView_loadMoreEnabled, true);
		int refreshFinalMoveOffset = a.getDimensionPixelOffset(R.styleable.IRecyclerView_refreshFinalMoveOffset, -1);
		if (refreshFinalMoveOffset != -1) {
			mRefreshFinalMoveOffset = refreshFinalMoveOffset;
		}
		a.recycle();

		if (mRefreshHeaderContainer == null) {
			mRefreshHeaderContainer = new RefreshHeaderLayout(getContext());
			mRefreshHeaderContainer.setLayoutParams(new RefreshHeaderLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0));
		}

		if (mLoadMoreFooterContainer == null) {
			mLoadMoreFooterContainer = new FrameLayout(getContext());
			mLoadMoreFooterContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		}

		mItemViewDelegateManager = new ItemViewDelegateManager();
		if (mRefreshEnabled) {//设置刷新header
			mItemViewDelegateManager.addDelegate(WrapperAdapter.REFRESH_HEADER, new RefreshHeaderItemDelegate());
			setRefreshHeaderView(mItemViewDelegateManager.getItemViewLayoutId(WrapperAdapter.REFRESH_HEADER));
		}
		if (mLoadMoreEnabled) {//设置上拉footer
			mItemViewDelegateManager.addDelegate(WrapperAdapter.LOAD_MORE_FOOTER, new LoadFooterItemDelegate());
			setLoadMoreFooterView(mItemViewDelegateManager.getItemViewLayoutId(WrapperAdapter.LOAD_MORE_FOOTER));
		}
		mItemViewDelegateManager.addDelegate(StatesAdapter.STATE_EMPTY, new EmptyViewDelagate());
		mItemViewDelegateManager.addDelegate(StatesAdapter.STATE_ERROR, new ErrorViewDelagate());
		mItemViewDelegateManager.addDelegate(StatesAdapter.STATE_LOADING, new EmptyViewDelagate());

		setRefreshEnabled(mRefreshEnabled);
		setLoadMoreEnabled(mLoadMoreEnabled);

		mScrollAnimator = new ValueAnimator();
		mScrollAnimator.addUpdateListener(mAnimatorUpdateListener);
		mScrollAnimator.addListener(mAnimationListener);

		setStatus(STATUS_DEFAULT);
	}

	@Override
	protected void onMeasure(int widthSpec, int heightSpec) {
		super.onMeasure(widthSpec, heightSpec);
		if (mRefreshHeaderView != null) {
			if (mRefreshHeaderView.getMeasuredHeight() > mRefreshFinalMoveOffset) {
				mRefreshFinalMoveOffset = 0;
			}
		}
	}

	public void setRefreshEnabled(boolean enabled) {
		mRefreshEnabled = enabled;
	}

	public void setLoadMoreEnabled(boolean enabled) {
		mLoadMoreEnabled = enabled;
		removeOnScrollListener(mOnLoadMoreScrollListener);
		if (mLoadMoreEnabled) {
			addOnScrollListener(mOnLoadMoreScrollListener);
		}
	}

	public void showLoadMoreView(boolean show) {
		if (show) {
			if (mLoadMoreFooterView != null) {
				mLoadMoreFooterContainer.getLayoutParams().height = mLoadMoreFooterView.getMeasuredHeight();
				mLoadMoreFooterContainer.requestLayout();
			}
		} else {
			mLoadMoreFooterContainer.getLayoutParams().height = 0;
			mLoadMoreFooterContainer.requestLayout();
		}
	}

	public void setOnRefreshListener(OnRefreshListener listener) {
		mOnRefreshListener = listener;
	}

	public void setOnLoadMoreListener(OnLoadMoreListener listener) {
		mOnLoadMoreListener = listener;
	}

	public void setRefreshing(boolean refreshing) {
		if (mStatus == STATUS_DEFAULT && refreshing) {//如果当前是默认状态，那么就到上拉加载状态
			mIsAutoRefreshing = true;
			setStatus(STATUS_SWIPING_TO_REFRESH);
			startScrollDefaultStatusToRefreshingStatus();
		} else if (mStatus == STATUS_REFRESHING && !refreshing) {//如果是刷新中状态
			mIsAutoRefreshing = false;
			startScrollRefreshingStatusToDefaultStatus();
		} else {//如果当前是刷新中状态，那么恢复到默认状态
			mIsAutoRefreshing = false;
			CLog.w(TAG, "isRefresh = " + refreshing + " current status = " + mStatus);
		}
	}

	public void setRefreshHeaderView(@LayoutRes int refreshHeaderLayoutRes) {
		final View refreshHeader = LayoutInflater.from(getContext()).inflate(refreshHeaderLayoutRes, mRefreshHeaderContainer, false);
		if (refreshHeader != null) {
			if (mRefreshHeaderView != null) {
				mRefreshHeaderContainer.removeView(mRefreshHeaderView);
			}
			if (mRefreshHeaderView != refreshHeader) {
				mRefreshHeaderView = refreshHeader;
				mRefreshHeaderContainer.addView(mRefreshHeaderView);
			}
		}
	}

	public void setLoadMoreFooterView(@LayoutRes int loadMoreFooterLayoutRes) {
		final View loadMoreFooter = LayoutInflater.from(getContext()).inflate(loadMoreFooterLayoutRes, mLoadMoreFooterContainer, false);
		if (loadMoreFooter != null) {
			if (mLoadMoreFooterView != null) {
				mLoadMoreFooterContainer.removeView(mLoadMoreFooterView);
			}
			if (mLoadMoreFooterView != loadMoreFooter) {
				mLoadMoreFooterView = loadMoreFooter;
				mLoadMoreFooterContainer.addView(loadMoreFooter);
			}
		}
	}


	@Override
	public boolean onInterceptTouchEvent(MotionEvent e) {
		final int action = MotionEventCompat.getActionMasked(e);
		final int actionIndex = MotionEventCompat.getActionIndex(e);
		switch (action) {
			case MotionEvent.ACTION_DOWN:
				mActivePointerId = MotionEventCompat.getPointerId(e, 0);
				mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
				mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
				break;

			case MotionEvent.ACTION_POINTER_DOWN:
				mActivePointerId = MotionEventCompat.getPointerId(e, actionIndex);
				mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
				mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
				break;

			case MotionEventCompat.ACTION_POINTER_UP:
				onPointerUp(e);
				break;
		}
		return super.onInterceptTouchEvent(e);
	}

	@Override
	public boolean onTouchEvent(MotionEvent e) {
		int index;
		final int action = MotionEventCompat.getActionMasked(e);
		switch (action) {
			case MotionEvent.ACTION_DOWN:
				index = MotionEventCompat.getActionIndex(e);
				mActivePointerId = MotionEventCompat.getPointerId(e, 0);
				mLastTouchX = getMotionEventX(e, index);
				mLastTouchY = getMotionEventY(e, index);
				break;

			case MotionEvent.ACTION_MOVE:
				index = MotionEventCompat.findPointerIndex(e, mActivePointerId);
				if (index < 0) {
					Log.e(TAG, "Error processing scroll; pointer index for id " + index + " not found. Did any MotionEvents get skipped?");
					return false;
				}

				final int x = getMotionEventX(e, index);
				final int y = getMotionEventY(e, index);

				final int dx = x - mLastTouchX;
				final int dy = y - mLastTouchY;

				mLastTouchX = x;
				mLastTouchY = y;

				final boolean triggerCondition = isEnabled() && mRefreshEnabled && mRefreshHeaderView != null && getScrollState() == SCROLL_STATE_DRAGGING && canTriggerRefresh();
				CLog.i(TAG, "triggerCondition = " + triggerCondition + "; mStatus = " + mStatus + "; dy = " + dy);
				if (triggerCondition) {
					final int refreshHeaderContainerHeight = mRefreshHeaderContainer.getMeasuredHeight();
					final int refreshHeaderViewHeight = mRefreshHeaderView.getMeasuredHeight();

					if (dy > 0 && mStatus == STATUS_DEFAULT) {
						setStatus(STATUS_SWIPING_TO_REFRESH);
						mRefreshTrigger.onStart(false, refreshHeaderViewHeight, mRefreshFinalMoveOffset);
					} else if (dy < 0) {
						if (mStatus == STATUS_SWIPING_TO_REFRESH && refreshHeaderContainerHeight <= 0) {
							setStatus(STATUS_DEFAULT);
						}
						if (mStatus == STATUS_DEFAULT) {
							break;
						}
					}

					if (mStatus == STATUS_SWIPING_TO_REFRESH || mStatus == STATUS_RELEASE_TO_REFRESH) {
						if (refreshHeaderContainerHeight >= refreshHeaderViewHeight) {
							setStatus(STATUS_RELEASE_TO_REFRESH);
							mRefreshTrigger.onMove(true, false, dy);
						} else {
							setStatus(STATUS_SWIPING_TO_REFRESH);
							mRefreshTrigger.onMove(false, false, dy);
						}
						fingerMove(dy);
						return true;
					}
				}
				break;

			case MotionEventCompat.ACTION_POINTER_DOWN:
				index = MotionEventCompat.getActionIndex(e);
				mActivePointerId = MotionEventCompat.getPointerId(e, index);
				mLastTouchX = getMotionEventX(e, index);
				mLastTouchY = getMotionEventY(e, index);
				break;

			case MotionEventCompat.ACTION_POINTER_UP:
				onPointerUp(e);
				break;

			case MotionEvent.ACTION_UP:
				onFingerUpStartAnimating();
				break;

			case MotionEvent.ACTION_CANCEL:
				onFingerUpStartAnimating();
				break;
		}
		return super.onTouchEvent(e);
	}


	public boolean canTriggerRefresh() {
		final Adapter adapter = getAdapter();
		if (adapter == null || adapter.getItemCount() <= 0) {
			return true;
		}
		View firstChild = getChildAt(0);
		int position = getChildLayoutPosition(firstChild);
		if (position == 0) {
			if (firstChild.getTop() == mRefreshHeaderContainer.getTop()) {
				return true;
			}
		}
		return false;
	}

	private int getMotionEventX(MotionEvent e, int pointerIndex) {
		return (int) (MotionEventCompat.getX(e, pointerIndex) + 0.5f);
	}

	private int getMotionEventY(MotionEvent e, int pointerIndex) {
		return (int) (MotionEventCompat.getY(e, pointerIndex) + 0.5f);
	}

	private void onFingerUpStartAnimating() {
		if (mStatus == STATUS_RELEASE_TO_REFRESH) {
			startScrollReleaseStatusToRefreshingStatus();
		} else if (mStatus == STATUS_SWIPING_TO_REFRESH) {
			startScrollSwipingToRefreshStatusToDefaultStatus();
		}
	}

	private void onPointerUp(MotionEvent e) {
		final int actionIndex = MotionEventCompat.getActionIndex(e);
		if (MotionEventCompat.getPointerId(e, actionIndex) == mActivePointerId) {
			// Pick a new pointer to pick up the slack.
			final int newIndex = actionIndex == 0 ? 1 : 0;
			mActivePointerId = MotionEventCompat.getPointerId(e, newIndex);
			mLastTouchX = getMotionEventX(e, newIndex);
			mLastTouchY = getMotionEventY(e, newIndex);
		}
	}

	private void fingerMove(int dy) {
		int ratioDy = (int) (dy * 0.5f + 0.5f);
		int offset = mRefreshHeaderContainer.getMeasuredHeight();
		int finalDragOffset = mRefreshFinalMoveOffset;

		int nextOffset = offset + ratioDy;
		if (finalDragOffset > 0) {
			if (nextOffset > finalDragOffset) {
				ratioDy = finalDragOffset - offset;
			}
		}

		if (nextOffset < 0) {
			ratioDy = -offset;
		}

		if (ratioDy != 0) {
			int height = mRefreshHeaderContainer.getMeasuredHeight() + ratioDy;
			mRefreshHeaderContainer.getLayoutParams().height = height;
			mRefreshHeaderContainer.requestLayout();
		}
	}

	private void startScrollDefaultStatusToRefreshingStatus() {
		mRefreshTrigger.onStart(true, mRefreshHeaderView.getMeasuredHeight(), mRefreshFinalMoveOffset);
		int targetHeight = mRefreshHeaderView.getMeasuredHeight();
		int currentHeight = mRefreshHeaderContainer.getMeasuredHeight();
		startScrollAnimation(400, new AccelerateInterpolator(), currentHeight, targetHeight);
	}

	private void startScrollSwipingToRefreshStatusToDefaultStatus() {
		final int targetHeight = 0;
		final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight();
		startScrollAnimation(300, new DecelerateInterpolator(), currentHeight, targetHeight);
	}

	private void startScrollReleaseStatusToRefreshingStatus() {
		mRefreshTrigger.onRelease();
		final int targetHeight = mRefreshHeaderView.getMeasuredHeight();
		final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight();
		startScrollAnimation(300, new DecelerateInterpolator(), currentHeight, targetHeight);
	}

	private void startScrollRefreshingStatusToDefaultStatus() {
		setStatus(STATUS_DEFAULT);//在N以下机型,无网络情况下,startScrollAnimation不会走listener的回调，因此手动设置status
		mRefreshTrigger.onComplete();
		final int targetHeight = 0;
		final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight();
		startScrollAnimation(400, new DecelerateInterpolator(), currentHeight, targetHeight);
	}

	private void startScrollAnimation(final int time, final Interpolator interpolator, int value, int toValue) {
		//在一加3(android7.0)，需要重新new一个mScrollAnimator，否则动画不消失
		if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
			mScrollAnimator = null;
			mScrollAnimator = new ValueAnimator();
		} else {
			mScrollAnimator.removeAllUpdateListeners();
			mScrollAnimator.removeAllListeners();
			mScrollAnimator.cancel();
		}
		mScrollAnimator.setIntValues(value, toValue);
		mScrollAnimator.setDuration(time);
		mScrollAnimator.addUpdateListener(mAnimatorUpdateListener);
		mScrollAnimator.addListener(mAnimationListener);
		mScrollAnimator.setInterpolator(interpolator);
		mScrollAnimator.start();
	}

	private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
			final int height = (Integer) animation.getAnimatedValue();
			mRefreshHeaderContainer.getLayoutParams().height = height;
			mRefreshHeaderContainer.requestLayout();
//			switch (mStatus) {
//				case STATUS_SWIPING_TO_REFRESH:
//					mRefreshTrigger.onMove(false, true, height);
//					break;
//				case STATUS_RELEASE_TO_REFRESH:
//					mRefreshTrigger.onMove(true, true, height);
//					break;
//				case STATUS_REFRESHING:
//					mRefreshTrigger.onMove(true, true, height);
//					break;
//			}
		}
	};

	private Animator.AnimatorListener mAnimationListener = new SimpleAnimatorListener() {
		@Override
		public void onAnimationEnd(Animator animation) {
			switch (mStatus) {
				case STATUS_SWIPING_TO_REFRESH: {
					if (mIsAutoRefreshing) {
						mRefreshHeaderContainer.getLayoutParams().height = mRefreshHeaderView.getMeasuredHeight();
						mRefreshHeaderContainer.requestLayout();
						setStatus(STATUS_REFRESHING);
						if (!((Activity) getContext()).isFinishing() && mOnRefreshListener != null) {
							mRefreshTrigger.onRefresh();
							mOnRefreshListener.onRefresh();
						}
					} else {
						mRefreshHeaderContainer.getLayoutParams().height = 0;
						mRefreshHeaderContainer.requestLayout();
						setStatus(STATUS_DEFAULT);
					}
				}
				break;

				case STATUS_RELEASE_TO_REFRESH: {
					mRefreshHeaderContainer.getLayoutParams().height = mRefreshHeaderView.getMeasuredHeight();
					mRefreshHeaderContainer.requestLayout();
					setStatus(STATUS_REFRESHING);
					if (!((Activity) getContext()).isFinishing() && mOnRefreshListener != null) {
						mRefreshTrigger.onRefresh();
						mOnRefreshListener.onRefresh();
					}
				}
				break;

				case STATUS_REFRESHING: {
					mIsAutoRefreshing = false;
					mRefreshHeaderContainer.getLayoutParams().height = 0;
					mRefreshHeaderContainer.requestLayout();
					setStatus(STATUS_DEFAULT);
					mRefreshTrigger.onReset();
				}
				break;
				case STATUS_DEFAULT:
					mIsAutoRefreshing = false;
					mRefreshHeaderContainer.getLayoutParams().height = 0;
					mRefreshHeaderContainer.requestLayout();
					setStatus(STATUS_DEFAULT);
					mRefreshTrigger.onReset();
					break;
			}
		}
	};

	private OnLoadMoreScrollListener mOnLoadMoreScrollListener = new OnLoadMoreScrollListener() {
		@Override
		public void onLoadMore(RecyclerView recyclerView) {
			if (mOnLoadMoreListener != null && mStatus == STATUS_DEFAULT) {
				//设置foot为loading状态
				if (mLoadMoreEnabled && mLoadMoreFooterContainer.getLayoutParams().height != 0) {
					setLoadMoreViewStatus(FooterLoadMoreView.Status.LOADING);
					mOnLoadMoreListener.onLoadMore();
				}
			}
		}
	};

	public void hideFooterLoadMoreView() {
		setLoadMoreViewStatus(FooterLoadMoreView.Status.GONE);
	}

	public void stopRefreshing() {
		setRefreshing(false);
		//setLoadMoreEnabled(true);
//		if (mRefreshHeaderContainer.getLayoutParams().height == 0 && mLoadMoreFooterView != null){
//			mLoadMoreFooterContainer.getLayoutParams().height = mLoadMoreFooterView.getMeasuredHeight();
//			mLoadMoreFooterContainer.requestLayout();
//		}
	}

	public void setStatus(int status) {
		mStatus = status;
	}

	public RefreshHeaderLayout getRefreshHeaderContainer() {
		return mRefreshHeaderContainer;
	}

	public FrameLayout getLoadMoreFooterContainer() {
		return mLoadMoreFooterContainer;
	}

	public ItemViewDelegateManager getItemViewDelegateManager() {
		return mItemViewDelegateManager;
	}

	public void setItemViewDelegateManager(ItemViewDelegateManager itemViewDelegateManager) {
		mItemViewDelegateManager = itemViewDelegateManager;
	}

	public void setLoadMoreViewStatus(FooterLoadMoreView.Status status) {
		if (mLoadMoreFooterView != null) {
			((FooterLoadMoreView) mLoadMoreFooterView).setStatus(status);
		}
	}

	private RefreshTrigger mRefreshTrigger = new RefreshTrigger() {
		@Override
		public void onStart(boolean automatic, int headerHeight, int finalHeight) {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onStart(automatic, headerHeight, finalHeight);
			}
		}

		@Override
		public void onMove(boolean finished, boolean automatic, int moved) {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onMove(finished, automatic, moved);
			}
		}

		@Override
		public void onRefresh() {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onRefresh();
			}
		}

		@Override
		public void onRelease() {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onRelease();
			}
		}

		@Override
		public void onComplete() {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onComplete();
			}
		}

		@Override
		public void onReset() {
			if (isRefreshTrigger()) {
				RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView;
				trigger.onReset();
			}
		}
	};

	private boolean isRefreshTrigger() {
		return mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger;
	}
}
